﻿using mozhi.smarterp.Finance.Enums;
using Mozhi.Abp.Core.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp;

namespace mozhi.smarterp.Finance.Finances
{
    public class Journal : DocumentAggregateRoot
    {
        public Guid NumberPrefixId { private set; get; }

        public int Period { private set; get; }

        public List<JournalItem> Items { private set; get; }

        public decimal TotalAmount { private set; get; }
        public decimal TotalAmountFr { private set; get; }

        public int AttachmentCount { private set; get; }

        public JournalSourceType SourceType { private set; get; }

        public string Keyword { private set; get; }

        public Journal(Guid id, Guid numberPrefixId, string number, DateTime date)
        {
            this.Id = id;
            this.NumberPrefixId = numberPrefixId;
            this.Number = number;
            this.Date = date;
            this.Period = date.ToPeriod();

            Items = new List<JournalItem>();

            Validate();
        }

        public void ChangeItems(List<JournalItem> items)
        {
            if (Status == DocumentStatusType.Approved)
                throw new UserFriendlyException("凭证已审核，不允许修改分录");

            foreach (var item in items)
            {
                var match = Items.FirstOrDefault(x => x.Id == item.Id);

                if (match != null)
                    match.Change(item.Summary, item.AccountId, item.JournalDirection, item.DimensionGroupValueId,
                    item.CurrencyId, item.ExchangeRate, item.DebitAmount, item.CreditAmount, item.Index);
                else
                    Items.Add(item);
            }

            var itemIds = items.Select(x => x.Id).ToList();

            var removedItems = Items.Where(x => !itemIds.Contains(x.Id)).ToList();

            foreach (var removedItem in removedItems)
            {
                Items.Remove(removedItem);
            }

            CheckItems();

            CheckAmountEqual();

            MergeItems();

            CalculateAmount();

        }

        protected void CalculateAmount()
        {
            if (Items == null || Items.Count == 0)
                throw new UserFriendlyException("凭证分录不能为空", "100000");

            this.TotalAmount = this.Items.Where(x => x.DebitAmount.HasValue).Sum(x => x.DebitAmount.Value);
            this.TotalAmountFr = this.Items.Where(x => x.DebitAmount.HasValue).Sum(x => x.AmountFr);
        }

        protected void CheckAmountEqual()
        {
            var debitAmountFr = this.Items.Where(x => x.DebitAmount.HasValue).Sum(x => x.DebitAmount.Value);
            var creditAmountFr = this.Items.Where(x => x.CreditAmount.HasValue).Sum(x => x.CreditAmount.Value);

            if (debitAmountFr != creditAmountFr)
                throw new UserFriendlyException("借方金额和贷方金额不相等", "100000");

        }

        protected void CheckItems()
        {
            if (Items == null || Items.Count < 2)
                throw new UserFriendlyException("凭证分录至少需要两项");
        }

        /// <summary>
        /// 审核
        /// </summary>
        /// <param name="approved"></param>
        public void ChangeApproveStatus(bool approved)
        {
            //if (this.Approved == approved)
            //    return;

            //this.Approved = approved;
            throw new NotImplementedException();
        }
        #region
        private void Validate()
        {
            if (NumberPrefixId == default)
                throw new UserFriendlyException("凭证字不能为空", "100000");

            if (Number.IsNullOrEmpty())
                throw new UserFriendlyException("编号不能为空", "100000");

            if (Date == default)
                throw new UserFriendlyException("日期不能为空", "100000");
        }
        private void MergeItems()
        {
            //合并规则，相同科目，相同币别，相同核算维度
        }
        #endregion
    }


    public class JournalItem : EntityBase<Guid>
    {
        public Guid JournalId { private set; get; }
        public string Summary { private set; get; }

        public Guid AccountId { private set; get; }

        public AccountDirectionType JournalDirection { private set; get; }

        public Guid? DimensionGroupValueId { private set; get; }

        public Guid CurrencyId { private set; get; }

        public decimal ExchangeRate { set; get; }

        /// <summary>
        /// 本位币金额
        /// </summary>
        public decimal Amount { set; get; }

        /// <summary>
        /// 原币金额
        /// </summary>
        public decimal AmountFr { set; get; }

        public decimal? DebitAmount { private set; get; }

        public decimal? CreditAmount { private set; get; }

        public int Index { set; get; }

        private JournalItem() { }

        public JournalItem(Guid id, string summary, Guid accountId, AccountDirectionType direction, Guid? dimensionGroupValueId,
            Guid currencyId, decimal exchangeRate, decimal amountFr, decimal? debitAmount, decimal? creditAmount, int index)
        {
            this.Id = id;
            this.Summary = summary;
            this.AccountId = accountId;
            this.JournalDirection = direction;
            this.DimensionGroupValueId = dimensionGroupValueId;
            this.CurrencyId = currencyId;
            this.ExchangeRate = exchangeRate;
            this.DebitAmount = debitAmount;
            this.CreditAmount = creditAmount;
            this.AmountFr = amountFr;
            this.Index = index;

            Validate();
            CalculateAmount();
        }

        public void CalculateAmount()
        {
            Amount = JournalDirection == AccountDirectionType.Debit ? this.DebitAmount.Value : this.CreditAmount.Value;
        }

        public void Change(string summary, Guid accountId, AccountDirectionType direction, Guid? dimensionGroupValueId,
            Guid currencyId, decimal exchangeRate, decimal? debitAmount, decimal? creditAmount, int index)
        {
            this.Summary = summary;
            this.AccountId = accountId;
            this.JournalDirection = direction;
            this.DimensionGroupValueId = dimensionGroupValueId;
            this.CurrencyId = currencyId;
            this.ExchangeRate = exchangeRate;
            this.DebitAmount = debitAmount;
            this.CreditAmount = creditAmount;
            this.Index = index;

            Validate();
            CalculateAmount();
        }

        public void ChangeAmount(decimal? debitAmount, decimal? creditAmount, int index)
        {
            this.DebitAmount = debitAmount;
            this.CreditAmount = creditAmount;
            this.Index = index;

            Validate();
            CalculateAmount();
        }

        public void ChangeAccount(Guid accountId)
        {
            this.AccountId = accountId;
        }

        #region
        private void Validate()
        {
            if (Summary.IsNullOrEmpty())
                throw new UserFriendlyException("摘要不能为空", "100000");

            if (AccountId == default)
                throw new UserFriendlyException("科目不能为空", "100000");

            if (JournalDirection == default)
                throw new UserFriendlyException("凭证方向不能为空", "100000");

            if (CurrencyId == default)
                throw new UserFriendlyException("币别不能为空", "100000");

            if (ExchangeRate == 0)
                throw new UserFriendlyException("汇率不能为空", "100000");

            if (Index == 0)
                throw new UserFriendlyException("分录序号不能为空", "100000");

            if (DebitAmount.HasValue && CreditAmount.HasValue)
                throw new UserFriendlyException("借方和贷方金额不能同时存在", "100000");

            if (JournalDirection == AccountDirectionType.Debit && DebitAmount == null)
                throw new UserFriendlyException("借方金额不能为空", "100000");

            if (JournalDirection == AccountDirectionType.Credit && CreditAmount == null)
                throw new UserFriendlyException("贷方金额不能为空", "100000");
        }
        #endregion
    }

}
